from itertools import groupby

from odoo import fields, models, _
from odoo.tools import float_is_zero
from odoo.exceptions import UserError


class PurchaseStockMoveReport(models.Model):
    _name = 's.purchase.stock.move.report'
    _description = 'Purchase Stock Move Report'
    _auto = False

    partner_id = fields.Many2one('res.partner', 'supplier')
    purchase_id = fields.Many2one('purchase.order', 'purchase')
    purchase_line_id = fields.Many2one('purchase.order.line', 'purchase line')
    move_id = fields.Many2one('stock.move', 'move')
    product_id = fields.Many2one('product.product', 'product')
    incoming_qty = fields.Float('incoming qty', digits='Product Unit of Measure')
    uom_id = fields.Many2one('uom.uom')
    done_date = fields.Datetime('done date')

    location_id = fields.Many2one('stock.location', 'Source Location')
    location_dest_id = fields.Many2one('stock.location', 'Destination Location')

    picking_id = fields.Many2one('stock.picking', 'Picking')
    delivery_no = fields.Char('Delivery No')

    def action_create_invoice(self):
        precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
        invoice_vals_list = []
        sequence = 10
        records = self.sorted(lambda x: (x.purchase_id.company_id.id, x.partner_id.id, x.purchase_id.currency_id))
        for group_key, g in groupby(records, key=lambda x: (x.purchase_id.company_id.id, x.partner_id.id, x.purchase_id.currency_id)):
            lines = self.concat(*g)
            purchase_id = lines[0].purchase_id
            purchase_id = purchase_id.with_company(purchase_id.company_id)
            pending_section = None
            invoice_vals = purchase_id._prepare_invoice()
            for line in lines:
                if line.purchase_line_id.display_type == 'line_section':
                    pending_section = line
                    continue
                if not float_is_zero(line.incoming_qty, precision_digits=precision):
                    if pending_section:
                        line_vals = pending_section._prepare_account_move_line()
                        line_vals.update({'sequence': sequence})
                        invoice_vals['invoice_line_ids'].append((0, 0, line_vals))
                        sequence += 1
                        pending_section = None
                    line_vals = line.purchase_line_id._prepare_account_move_line()
                    qty = line.uom_id._compute_quantity(line.incoming_qty, line.purchase_line_id.product_uom)
                    line_vals.update({
                        'sequence': sequence,
                        'quantity': qty,
                        's_stock_move_id': line.move_id.id
                    })
                    invoice_vals['invoice_line_ids'].append((0, 0, line_vals))
                    sequence += 1
            invoice_vals_list.append(invoice_vals)

        if not invoice_vals_list:
            raise UserError(_('There is no invoiceable line. If a product has a control policy based on received quantity, please make sure that a quantity has been received.'))

        new_invoice_vals_list = []
        for grouping_keys, invoices in groupby(invoice_vals_list, key=lambda x: (x.get('company_id'), x.get('partner_id'), x.get('currency_id'))):
            origins = set()
            payment_refs = set()
            refs = set()
            ref_invoice_vals = None
            for invoice_vals in invoices:
                if not ref_invoice_vals:
                    ref_invoice_vals = invoice_vals
                else:
                    ref_invoice_vals['invoice_line_ids'] += invoice_vals['invoice_line_ids']
                origins.add(invoice_vals['invoice_origin'])
                payment_refs.add(invoice_vals['payment_reference'])
                refs.add(invoice_vals['ref'])
            ref_invoice_vals.update({
                'ref': ', '.join(refs)[:2000],
                'invoice_origin': ', '.join(origins),
                'payment_reference': len(payment_refs) == 1 and payment_refs.pop() or False,
            })
            new_invoice_vals_list.append(ref_invoice_vals)
        invoice_vals_list = new_invoice_vals_list

        moves = self.env['account.move']
        AccountMove = self.env['account.move'].with_context(default_move_type='in_invoice')
        for vals in invoice_vals_list:
            moves |= AccountMove.with_company(vals['company_id']).create(vals)

        moves.filtered(lambda m: m.currency_id.round(m.amount_total) < 0).action_switch_move_type()

        return self.env['purchase.order'].action_view_invoice(moves)

    @property
    def _table_query(self):
        sql = """
            select po.id       purchase_id,
                   pol.id      purchase_line_id,
                   sm.id       move_id,
                   sm.product_id product_id,
                   sm.date done_date,
                   case when sl2.usage = 'supplier' and sl.usage = 'internal' then sm.quantity * -1
                    else sm.quantity end incoming_qty,
                   sm.product_uom uom_id,
                   sm.id id,
                   sp.delivery_no delivery_no,
                   sm.picking_id picking_id,
                   po.partner_id partner_id,
                   sm.location_id location_id,
                   sm.location_dest_id location_dest_id
            from stock_move sm
                     left join purchase_order_line pol on sm.purchase_line_id = pol.id
                     left join purchase_order po on pol.order_id = po.id
                     left join stock_picking sp on sp.id = sm.picking_id
                     left join stock_location sl on sl.id = sm.location_id
                     left join stock_location sl2 on sl2.id = sm.location_dest_id
            where sm.state = 'done'
              and sm.purchase_line_id is not null
              and sm.product_uom_qty > s_qty_invoiced
        """
        return sql